package mws;

import lombok.Data;
import lombok.Setter;


import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;



public abstract class AbstractLimiter implements Limiter {

    private ScheduledExecutorService executorService ;

    private AtomicInteger count ;

    private volatile int maxNum = 0;
    @Setter
    private volatile int incRate = 1;
    @Setter
    private volatile int waitingNum = 0;
    @Setter
    private volatile int waitingMaxNum = 0;

    private final Object lock = new Object();
    private final Object lockV2 = new Object();

    private  AtomicLong lastRefillTime ;


    public AbstractLimiter(int incRate,int limiter){
        this.incRate = incRate;
        this.maxNum = limiter;
    }

    public void setDelayTime(int time, TimeUnit timeUnit){
        // todo 我在写令牌桶 然后要实现动态切换 这里流量激增怎么办
        ScheduledExecutorService scheduledExecutorService = executorService;
        executorService = Executors.newSingleThreadScheduledExecutor();
        executorService.scheduleAtFixedRate(new TaskOnTime(),time/10,time,timeUnit);
        if(scheduledExecutorService!=null){
            scheduledExecutorService.shutdown();
        }
    }
    public  void setMaxNum(int maxNum){

        synchronized (lock){
            int num = this.maxNum;
            this.maxNum = maxNum;
            int diff = num - maxNum;
            while (true) {
                int i = count.get();
                if (count.compareAndSet(i,i + diff)) {
                    break;
                }
            }
        }

    }
    public void start(){
        lastRefillTime = new AtomicLong(System.nanoTime());
        count = new AtomicInteger(0);
    }
    //  多线程进行截断
    public boolean tryAcquire(){
        int current = count.decrementAndGet();
        if(current < 0){
            count.incrementAndGet();
            return false;
        }
        return true;
    };

// 改成一个 有先后顺序的队列
    public int acquire() {
        int waitingMaxNum = 0;
        while (!tryAcquire()) {
            if(waitingMaxNum > this.waitingMaxNum){
                return 0;
            }
            LockSupport.parkNanos(100);
            waitingMaxNum++;
        }
        return 1;
    }
//    public int acquire(){
//
//        BlokingQueueNode node = null;
//        while(!tryAcquire()){
//            if(node == null){
//                node = new BlokingQueueNode();
//                node.thread = Thread.currentThread();
//            } else if(node.getState() == 0){
//                node.state = BlokingQueueNode.State.BLOCKING;
//            }else if (node.getState() == 1){
//                synchronized (lock){
//                        if(waitingNum + 1<= waitingMaxNum){
//                            waitingNum++;
//                            node.state = BlokingQueueNode.State.BLOCKING;
//                        }else{
//                            count.incrementAndGet();
//                            node.state = BlokingQueueNode.State.DONE;
//                        }
//                }
//            }else {
//                LockSupport.parkNanos(100);
//            }
//        }
//        if(node!=null){
//            node.state = BlokingQueueNode.State.DONE;
//            node = null;
//        }
//
//        return 1;
//    };


   class TaskOnTime implements Runnable{
       @Override
       public void run() {
          synchronized (lock){
              int current = count.get();
              int incNum = incRate;
              if(current + incNum <= maxNum){
                  count.addAndGet(incNum);
              }else {
                  count.addAndGet(maxNum - current);
              }
          }
       }
   }

   @Data
   class BlokingQueueNode{
       Thread thread;
       BlokingQueueNode next;
       State state = State.NEW;
       enum State{
           NEW,
           BLOCKING,
           DONE
       }

       public int getState(){
           return state.ordinal();
       }
   }



}
